/*
 * SMPP Encoder/Decoder
 * Copyright (C) 2006 redtaza@users.sourceforge.net
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */
#ifndef __SMPP_AUX_TYPES_HPP__
#define __SMPP_AUX_TYPES_HPP__

/// @file aux_types.h
/// @brief Defines a number of SMPP specific types and auxiliary functions.

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <list>
#include <iomanip>
#include <iostream>
#include "error.h"

namespace Smpp {
    typedef char Char;
    typedef unsigned char Uint8;
    typedef unsigned short int Uint16;
    typedef unsigned int Uint32;
    typedef std::basic_string<Char> String;

    inline Smpp::Uint32 ntoh32(const Smpp::Uint32& n);
    inline Smpp::Uint32 hton32(const Smpp::Uint32& n);
    inline Smpp::Uint16 ntoh16(const Smpp::Uint16& n);
    inline Smpp::Uint16 hton16(const Smpp::Uint16& n);

    inline const Smpp::String& valid_length(const Smpp::String&, size_t, const char* err);
    inline const Smpp::Char* valid_length(const Smpp::Char*, size_t, const char* err);
   
    void chex_dump(const Smpp::Uint8* buff, Smpp::Uint32 len, FILE* file);
    void hex_dump(const Smpp::Uint8* buff, Smpp::Uint32 len, std::ostream& os);

    //
    // Functions that check the length of strings
    //

    /// @brief Checks the length of a Smpp::String.
    /// @param s The string.
    /// @param len The allowed length including the NULL terminator.
    /// @param err Message thrown in exception if length is invalid.
    /// @throw Smpp::Error Containing the err string.
    inline const Smpp::String&
    valid_length(const Smpp::String& s, size_t len, const char* err) {
        if(s.length() >= len)
            throw Error(err);
        return s;
    }
        
    /// @brief Checks the length of a Smpp::Char*
    /// @param s The Smpp::Char* string.
    /// @param len The allowed length including the NULL terminator.
    /// @param err Message thrown in exception if length is invalid.
    /// @throw Smpp::Error Containing the err string.
    inline const Smpp::Char*
    valid_length(const Smpp::Char* s, size_t len, const char* err) {
        for(size_t i=0; i < len; ++i)
            if(s[i] == '\0')
                return s;
        throw Error(err);
    }

    //-------------------------------------------------------------------------
    // Inline functions that return header parameters.
    // All are passed a pointer that should point to the start of an
    // SMPP encoded PDU.
    //-------------------------------------------------------------------------

    /// @brief Access the command_length from an encoded SMPP PDU.
    /// @param b Pointer to start of encoded SMPP PDU.
    /// @return The decoded command_length.
    inline Smpp::Uint32 get_command_length(const Smpp::Uint8* b) {
        Smpp::Uint32 x;
        std::copy(&b[0], &b[4], reinterpret_cast<Smpp::Uint8*>(&x));
        return Smpp::ntoh32(x);
    }

    /// @brief Access the command_id from an encoded SMPP PDU.
    /// @param b Pointer to start of encoded SMPP PDU.
    /// @return The decoded command_id.
    inline Smpp::Uint32 get_command_id(const Smpp::Uint8* b) {
        Smpp::Uint32 x;
        std::copy(&b[4], &b[8], reinterpret_cast<Smpp::Uint8*>(&x));
        return Smpp::ntoh32(x);
    }

    /// @brief Access the command_status from an encoded SMPP PDU.
    /// @param b Pointer to start of encoded SMPP PDU.
    /// @return The decoded command_status.
    inline Smpp::Uint32 get_command_status(const Smpp::Uint8* b) {
        Smpp::Uint32 x;
        std::copy(&b[8], &b[12], reinterpret_cast<Smpp::Uint8*>(&x));
        return Smpp::ntoh32(x);
    }

    /// @brief Access the sequence_number from an encoded SMPP PDU.
    /// @param b Pointer to start of encoded SMPP PDU.
    /// @return The decoded sequence_number.
    inline Smpp::Uint32 get_sequence_number(const Smpp::Uint8* b) {
        Smpp::Uint32 x;
        std::copy(&b[12], &b[16], reinterpret_cast<Smpp::Uint8*>(&x));
        return Smpp::ntoh32(x);
    }

    //-------------------------------------------------------------------------
    // Inline functions that convert to/from host and network byte order.
    //-------------------------------------------------------------------------

    /// @brief Returns true if the current machine is big endian.
    /// @return true if big endian or false if little endian.
    inline bool is_big_endian() // Big Endian is network byte order.
    {
        const long x = 1;
        return *(const char*)&x ? false : true;
    }

    /// @brief Converts a 32 bit integer to network byte order.
    /// @param n The integer in host byte order.
    /// @return The integer in network byte order.
    inline Smpp::Uint32 ntoh32(const Smpp::Uint32& n)
    {
        if(is_big_endian())
            return n;
        return (n & 0x000000ff) << 24 |
               (n & 0x0000ff00) << 8 |
               (n & 0x00ff0000) >> 8 |
               (n & 0xff000000) >> 24;
    }

    /// @brief Converts a 32 bit integer to host byte order.
    /// @param n The integer in network byte order.
    /// @return The integer in host byte order.
    inline Smpp::Uint32 hton32(const Smpp::Uint32& n)
    {
        if(is_big_endian())
            return n;
        return (n & 0x000000ff) << 24 |
               (n & 0x0000ff00) << 8 |
               (n & 0x00ff0000) >> 8 |
               (n & 0xff000000) >> 24;
    }

    /// @brief Converts a 16 bit integer to network byte order.
    /// @param n The integer in host byte order.
    /// @return The integer in network byte order.
    inline Smpp::Uint16 ntoh16(const Smpp::Uint16& n)
    {
        if(is_big_endian())
            return n;
        return (n & 0x00ff) << 8 | (n & 0xff00) >> 8;
    }

    /// @brief Converts a 16 bit integer to host byte order.
    /// @param n The integer in network byte order.
    /// @return The integer in host byte order.
    inline Smpp::Uint16 hton16(const Smpp::Uint16& n)
    {
        if(is_big_endian())
            return n;
        return (n & 0x00ff) << 8 | (n & 0xff00) >> 8;
    }

    /// @brief Convert an octet stream into a 16 bit integer.
    /// @param b The octet stream (must be at least 2 bytes).
    /// @return The 16 bit integer.
    inline Smpp::Uint16 ntoh16(const Smpp::Uint8* b)
    {
        Smpp::Uint16 t;
        std::copy(b, b+2, (Smpp::Uint8*)&t);
        return Smpp::ntoh16(t);
    }
    
    /// @brief Convert an octet stream into a 32 bit integer.
    /// @param b The octet stream (must be at least 4 bytes).
    /// @return The 32 bit integer.
    inline Smpp::Uint32 ntoh32(const Smpp::Uint8* b)
    {
        Smpp::Uint32 t;
        std::copy(b, b+4, (Smpp::Uint8*)&t);
        return Smpp::ntoh32(t);
    }

	
    class Integer1{
        Smpp::Uint8 v_;
      public:

        explicit Integer1(const Smpp::Uint8& p = 0x00) : v_(p) {}
        Integer1& operator=(const Smpp::Uint8& p) { v_ = p;  return *this; }
        Smpp::Uint8 value() const { return v_; }
		Smpp::Uint8 decode(Smpp::Char* &buff)
        {
            Smpp::Uint8 id;
            
            memcpy(&id, buff, sizeof id);
			v_=id;
			buff+=1;
            return v_;
        }
    };
	
    class Integer2{
        Smpp::Uint16 v_;
      public:

        explicit Integer2(const Smpp::Uint16& p = 0x00) : v_(p) {}
        Integer2& operator=(const Smpp::Uint16& p) { v_ = p;  return *this; }
        Smpp::Uint16 value() const { return v_; }
		Smpp::Uint16 decode(Smpp::Char* &buff)
        {
            Smpp::Uint16 id;
            
            memcpy(&id, buff, sizeof id);
			v_=Smpp::ntoh16(id);
			buff+=2;
            return v_;
        }
    };
	
    class Integer4{
        Smpp::Uint32 v_;
      public:

        explicit Integer4(const Smpp::Uint32& p = 0x00) : v_(p) {}
        Integer4& operator=(const Smpp::Uint32& p) { v_ = p;  return *this; }
        Smpp::Uint32 value() const { return v_; }
		Smpp::Uint32 decode(Smpp::Char* &buff)
        {
            Smpp::Uint32 id;
            
            memcpy(&id, buff, sizeof id);
			v_=Smpp::ntoh32(id);
			buff+=4;
            return v_;
        }
    };

	class OctetString {
		Smpp::String v_;
	  public:
	  	Smpp::Uint32 MaxLength;

		OctetString() { MaxLength=0; }
		void setMaxLength(int len) { MaxLength=len; }
		OctetString& operator=(const char* p) {
			v_ = valid_length(p, MaxLength+1, "Invalid OctetString length");
			return *this;
		}
		const Smpp::Uint8* to_uint8() const { return (Smpp::Uint8*)(v_.c_str()); }
		const char* c_str() const { return v_.c_str(); }
		size_t length() const { return v_.length(); }
		void decode(Smpp::Char* &buff)
		{
//			v_ = valid_length(buff, MaxLength+1, "Invalid OctetString length");
//			buff+=(MaxLength+1);
			char temp[256];
			memcpy(temp,buff,MaxLength);
			temp[MaxLength]='\0';
			v_ = valid_length(temp, MaxLength+1, "Invalid OctetString length");
			buff+=(MaxLength);

		}
	};

	class C_OctetString {
		Smpp::String v_;
	  public:

		C_OctetString() {}
		C_OctetString& operator=(const char* p) {
			v_ = p;
			return *this;
		}
        Smpp::String value() const { return v_; }
		const Smpp::Uint8* to_uint8() const { return (Smpp::Uint8*)(v_.c_str()); }
		const char* c_str() const { return v_.c_str(); }
		size_t length() const { return v_.length(); }
		void decode(Smpp::Char* &buff)
		{
			v_ = buff;
			buff+=(v_.length()+1);

		}
	};


} // namespace Smpp


#endif

